const nsIglPHProxyManager = Components.interfaces.nsIglPHProxyManager;
const nsISupports = Components.interfaces.nsISupports;
const CLASS_ID = Components.ID("{0e064837-965c-47c0-ad7c-9606393bfd63}");
const CLASS_NAME = "Manager of PHProxies List of Gladder";
const CONTRACT_ID = "@gladder.gneheix.com/phproxymanager;1";
const CC = Components.classes;
const CI = Components.interfaces;
const GL_PHPROXY_STATUS_FIALED_TO_CONNECT = -2;
const GL_PHPROXY_STATUS_NOT_WORKING = -3;
const GL_PHPROXY_STATUS_UNKNOWN = -1;

function PHProxyManager() {
	this._init();
};

PHProxyManager.prototype = {
	_list: [], //list of proxies
	_listUsed: [],
	_listAll: [], //list of all cadidates of proxies
	_listFailed: [],
	_firsttime: true,
	_ready: false,
	_pref : null,

	get ready(){
		return this._ready}, //is ready to work
	
	proxy: function(url) {
		try{
			if (!this._ready){
				return url;
			}
			var i = this._getFastOneIndex();
			if (i != -1){
				var p = this._list[i];
				if (this._listUsed.indexOf(p) == -1){
					this._listUsed[this._listUsed.length] = p;
				}
				return p.proxy(url);
			}
		}catch(e){}
		return url;
	},
	_getFastOneIndex: function(){
		var i = -1;
		for(var index=0; index<this._list.length; index++) {
			if (this._list[index].response > 0 && (i == -1 || this._list[index].response < this._list[i].response)){
				i = index;
			}
		}
		if (i == -1){
			this._setReady(false);
		}
		return i;		
	},
	unproxy: function(url){
		try{
			for(var index=0; index<this._list.length; index++) {
				var u = this._list[index].unproxy(url);
				if (u != url){
					return u; 
				}
			}
			for(var index=0; index<this._listUsed.length; index++) {
				var u = this._listUsed[index].unproxy(url);
				if (u != url){
					return u; 
				}
			}
		}catch(e){}
		return url;
	},
	
	findProxy: function(url){
		try{
			for(var index=0; index<this._list.length; index++) {
				if (this._list[index].unproxy(url) != url){
					return this._list[index]; 
				}
			}
			for(var index=0; index<this._listUsed.length; index++) {
				if (this._listUsed[index].unproxy(url) != url){
					return this._listUsed[index];
				}
			}
		}catch(e){}
		return null;
	},
	removeCurrentOne: function(){
		try{
			this._list.splice(this._getFastOneIndex(), 1);
			this._checkListSize();
		}catch(e){}
	},
	openFailed: function (url){
		try{
			for(var index=0; index<this._list.length; index++) {
				var u = this._list[index].unproxy(url);
				if (u != url){
					if (!this._network.syncTest()){
						return url;
					}
					this._list.splice(index, 1);
					this._checkListSize();
					u = this.proxy(u);
					if (this.ready){
						return u;
					}
				}
			}
			for(var index=0; index<this._listUsed.length; index++) {
				var u = this._listUsed[index].unproxy(url);
				if (u != url){
					if (!this._network.syncTest()){
						return url;
					}
					u = this.proxy(u);
					if (this.ready){
						return u;
					}
				}
			}
		}catch(e){dump("glPHProxyMananger openFailed process error: "+e+e.description+"\n");}
		return url;
	},
	getCurrentProxy: function(){
		var index = this._getFastOneIndex();
		if (index != -1){
			return this._list[index];
		}
		return null;
	},
	_init: function(){
		CC["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService).addObserver(this, "gl-phproxy-speed", false);
		CC["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService).addObserver(this, "gl-network-status", false);
		CC["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService).addObserver(this, "gl-network-status-testing-result", false);
		this._pref =  CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch);
		this._network =  CC["@gladder.gneheix.com/NetworkStatus;1"].getService(CI.nsIglNetworkStatus);
		this._loadFromPref();
		this._checkListSize();
	},
	_setReady: function(ready){
		if (this._ready != ready){
			CC["@mozilla.org/observer-service;1"].
			getService(CI.nsIObserverService).
			notifyObservers(this, "gl-phproxy-mananger-ready", ready);	
		}
		this._ready = ready;
	},
	_loadFromPref: function(){
		var pref = this._pref.getCharPref("gladder.phproxy.list");
		if (pref){
			var list = pref.split("|");
			for(var index=0; index<list.length; index++) {
				var domain = list[index];
				if (!domain)continue;
				var proxy = CC["@gladder.gneheix.com/phproxy;1"].createInstance(CI.nsIglPHProxy);
				proxy.domain = domain;
				this._list[this._list.length] = proxy;
			}
		}
		pref = this._pref.getCharPref("gladder.phproxy.listall");
		if (pref){
			this._listAll = pref.split("|");
			if (this._pref.getIntPref("gladder.phproxy.listall.lastdate") != new Date().getDate()){
				this._pref.setIntPref("gladder.phproxy.listall.lastdate", new Date().getDate());
				this._getListAll();
			}
		} 
	},
	_checkListSize: function(){
		if (this._list.length == 0){
			this._setReady(false);
		}
		if ((this._list.length + this._listFailed.length)< this._pref.getIntPref("gladder.phproxy.list.size")){
			if (this._listAll.length > 0){
				while((this._list.length + this._listFailed.length) <  this._pref.getIntPref("gladder.phproxy.list.size")){
					var p = CC["@gladder.gneheix.com/phproxy;1"].createInstance(CI.nsIglPHProxy);
					p.domain = this._listAll[Math.floor(Math.random()*this._listAll.length)];
					this._list[this._list.length] = p;
					var listDomain = [];
					for(var index=0; index<this._list.length; index++) {
						listDomain[listDomain.length] = this._list[index].domain;
					}
					this._pref.setCharPref("gladder.phproxy.list", listDomain.join("|"));
				}
			}
//			else{
//				this._getListAll();
//			}
		}
	},
	_removeBadOnes: function(){
		for(var index=0; index<this._list.length; index++) {
			if (this._list[index].response < -1){
				this._list.splice(index, 1);
			}
		}
	},
	_readListAll: function(aData, aRequest)
  {
    var httpChannel = aRequest.QueryInterface(CI.nsIHttpChannel);
    
    var network = true;
    var status = null;
    var statusText = null;
    
    try {
      // Get HTTP response
      status = httpChannel.responseStatus;
      statusText = httpChannel.responseStatusText;
    } catch (e) {
      network = false;
    }
    if (!network) // Network was disconnected
    {
    	if (!this._ready){
   			this._setTimer(1); //retry in one minute
    	}
      return;
    }
    else if (status == null || status != 200) 
    {
    	if (!this._ready){
   			this._setTimer(30); // server error, retry in half hour
    	}
      return;
    }
    else{
		this._listAll = aData.replace(/\r\n/g, "\n").split("\n");
		this._gettingListAll = false;
		for(var index=0; index<this._listAll.length; index++) {
			if (!/([\w-]+\.)+[\w-]+/.test(this._listAll[index])){
				this._listAll.splice(index, 1);
			}
		}
		this._pref.setCharPref("gladder.phproxy.listall", this._listAll.join("|"));
		this._checkListSize();
    }
  },
	_getListAll: function(){
		try {
			this._gettingListAll = true;
			this._setTimer(5);
			if (!this._network.online)return;
		    var ioService = CC["@mozilla.org/network/io-service;1"].createInstance(CI.nsIIOService);
		    var uri = ioService.newURI(this._pref.getCharPref("gladder.phproxy.listall.url"), null, null);
		    var channel = ioService.newChannelFromURI(uri);
		    
		    channel.asyncOpen(new this._observer(this, true), null);
		}
		catch (e) {dump("glPHProxyMananger._getListAll error:"+e+e.description+"\n")}
	},
	observe: function(aSubject, aTopic, aData){
		if (aTopic == "gl-phproxy-speed"){
			if (aData < GL_PHPROXY_STATUS_UNKNOWN){
				var index = this._list.indexOf(aSubject);
				if (index != -1){
					this._list.splice(index, 1);
					this._listFailed[this._listFailed.length] = aSubject;
					this._network.asyncTest(); //invoke test
					if (this._list.length == 0)
						this._setReady(false);
				}
			}
		else if (aData > GL_PHPROXY_STATUS_UNKNOWN){
				this._setReady(true);
				CC["@mozilla.org/observer-service;1"].getService(CI.nsIObserverService).notifyObservers(aSubject, "gl-phproxy-list-ready", aData);
			}
		}
		else if (aTopic == "gl-network-status"){
			try{
				if (aData == "true"){
					if (this._gettingListAll){
						this._getListAll();
					}
					for(var index=0; index<this._list.length; index++) {
						if (this._list[index].response > 0){
							this._setReady(true);
						}
					}
				}
				else{
					this._setReady(false);
				}
			}
			catch(e){}
		}
		else if (aTopic == "gl-network-status-testing-result"){
			try{
				if (aData == "true"){
					for(var index=0; index<this._listFailed.length; index++) {
						var i = this._listAll.indexOf(this._listFailed[index].domain);
						if (i != -1){
							this._listAll.splice(i , 1);
						}
					}
					if (this._listAll.length == 0){
						this._getListAll();
					}
					this._removeBadOnes();
					this._listFailed = [];
					this._checkListSize();
				}
				else{
					this._list = this._list.concat(this._listFailed);
					this._listFailed = [];
				}
			}catch(e){}
		}
	},
    _setTimer: function(minutes)
  {
    if (this._timer) {
      this._timer.cancel();
      this._timer = null;
    }
    
    if (minutes > 0) {
      try {
        this._interval = minutes;
        this._timer = GM_CC["@mozilla.org/timer;1"].createInstance(GM_CI.nsITimer);
        this._timer.initWithCallback(this, minutes * 60000, this._timer.TYPE_ONE_SHOT);
      } catch(e) {}
    }
  },
  
  notify: function(aTimer)
  {
  	if (this._gettingListAll){
      this._getListAll();
  	}
  },
  _observer: function(aThis)
  {
    return ({
      _myData: "",
      
      onStartRequest: function(aRequest, aContext) {
        this._myData = "";
      },
      
      onStopRequest: function(aRequest, aContext, aStatus) {
        	aThis._readListAll(this._myData, aRequest);
      },
      
      onDataAvailable: function(aRequest, aContext, aStream, aSourceOffset, aLength) {
        var scriptableInputStream = CC["@mozilla.org/scriptableinputstream;1"].createInstance(CI.nsIScriptableInputStream);
        scriptableInputStream.init(aStream);
        this._myData += scriptableInputStream.read(aLength);
      }});
  	},
  QueryInterface: function(aIID)
  {
    if (!aIID.equals(nsIglPHProxyManager) && 
    	!aIID.equals(CI.nsIObserver) &&
        !aIID.equals(nsISupports))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};

var PHProxyManagerFactory = {
  createInstance: function (aOuter, aIID)
  {
    if (aOuter != null)
      throw Components.results.NS_ERROR_NO_AGGREGATION;
    return (new PHProxyManager()).QueryInterface(aIID);
  }
};

var PHProxyManagerModule = {
  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType)
  {
    aCompMgr = aCompMgr.
        QueryInterface(Components.interfaces.nsIComponentRegistrar);
    aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, 
        CONTRACT_ID, aFileSpec, aLocation, aType);
  },

  unregisterSelf: function(aCompMgr, aLocation, aType)
  {
    aCompMgr = aCompMgr.
        QueryInterface(Components.interfaces.nsIComponentRegistrar);
    aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);        
  },
  
  getClassObject: function(aCompMgr, aCID, aIID)
  {
    if (!aIID.equals(Components.interfaces.nsIFactory))
      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;

    if (aCID.equals(CLASS_ID))
      return PHProxyManagerFactory;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  canUnload: function(aCompMgr) { return true; }
};

function NSGetModule(aCompMgr, aFileSpec) { return PHProxyManagerModule; }